#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
多平台商品爬虫 GUI 监听器
集成 PDD、JD、TaoBao、DY 四个平台的 Redis 任务监听和处理
"""

import os
import sys
import json
import time
import redis
import pickle
import logging
import threading
import traceback
import importlib
from datetime import datetime
from typing import Dict, List, Any, Optional
from queue import Queue, Empty

# GUI相关导入
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
from tkinter.messagebox import askyesno, showinfo, showwarning

from selenium.webdriver.common.by import By


# 平台爬虫导入 - 使用更健壮的导入方式
def safe_import_platform_modules():
    """安全导入各平台模块"""
    modules = {
        'PDD': {'scraper': None, 'config': None},
        'JD': {'scraper': None, 'config': None},
        'TaoBao': {'scraper': None, 'config': None},
        'DY': {'scraper': None, 'config': None}
    }
    
    # 添加当前目录到Python路径
    current_dir = os.path.dirname(os.path.abspath(__file__))
    if current_dir not in sys.path:
        sys.path.insert(0, current_dir)
    
    # PDD模块导入
    try:
        pdd_scraper_module = importlib.import_module('PDD.scraper')
        pdd_config_module = importlib.import_module('PDD.config')
        modules['PDD']['scraper'] = getattr(pdd_scraper_module, 'PDDScraper', None)
        modules['PDD']['config'] = getattr(pdd_config_module, 'PDDConfig', None)
        print("✅ PDD模块导入成功")
    except Exception as e:
        print(f"❌ PDD模块导入失败: {e}")
    
    # JD模块导入
    try:
        jd_scraper_module = importlib.import_module('JD.scraper_clean')
        jd_config_module = importlib.import_module('JD.config')
        modules['JD']['scraper'] = getattr(jd_scraper_module, 'JDScraper', None)
        modules['JD']['config'] = getattr(jd_config_module, 'JDConfig', None)
        print("✅ JD模块导入成功")
    except Exception as e:
        print(f"❌ JD模块导入失败: {e}")
    
    # TaoBao模块导入
    try:
        taobao_scraper_module = importlib.import_module('TaoBao.scraper')
        taobao_config_module = importlib.import_module('TaoBao.config')
        modules['TaoBao']['scraper'] = getattr(taobao_scraper_module, 'TaoBaoScraper', None)
        modules['TaoBao']['config'] = getattr(taobao_config_module, 'TaoBaoConfig', None)
        print("✅ TaoBao模块导入成功")
    except Exception as e:
        print(f"❌ TaoBao模块导入失败: {e}")
    
    # DY模块导入
    try:
        dy_scraper_module = importlib.import_module('DY.scraper')
        dy_config_module = importlib.import_module('DY.config')
        modules['DY']['scraper'] = getattr(dy_scraper_module, 'DYScraper', None)
        modules['DY']['config'] = getattr(dy_config_module, 'DYConfig', None)
        print("✅ DY模块导入成功")
    except Exception as e:
        print(f"❌ DY模块导入失败: {e}")
    
    return modules

# 执行安全导入
platform_modules = safe_import_platform_modules()

# 为了向后兼容，设置全局变量
PDDScraper = platform_modules['PDD']['scraper']
PDDConfig = platform_modules['PDD']['config']
JDScraper = platform_modules['JD']['scraper']
JDConfig = platform_modules['JD']['config']
TaoBaoScraper = platform_modules['TaoBao']['scraper']
TaoBaoConfig = platform_modules['TaoBao']['config']
DYScraper = platform_modules['DY']['scraper']
DYConfig = platform_modules['DY']['config']

class MultiPlatformGUIMonitor:
    """多平台GUI监听器"""
    
    def __init__(self, user_token):
        """初始化GUI监听器"""
        self.user_token = user_token  # 用户令牌
        
        self.root = tk.Tk()
        self.root.title("水滴多平台商品爬虫监听器")
        self.root.geometry("1200x800")
        
        # 监听状态
        self.is_monitoring = False
        self.monitoring_thread = None
        self.stop_event = threading.Event()
        
        # Redis客户端
        self.redis_clients = {}
        
        # 平台配置
        self.platform_configs = {
            "拼多多": {
                "scraper_class": PDDScraper,
                "config_class": PDDConfig,
                "redis_config": {
                    'host': '8.155.9.53',
                    'port': 6300,
                    'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
                    'task_key': ':1:自动获取产品列表'
                }
            },
            "京东": {
                "scraper_class": JDScraper,
                "config_class": JDConfig,
                "redis_config": {
                    'host': '8.155.9.53',
                    'port': 6300,
                    'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
                    'task_key': ':1:自动获取产品列表'
                }
            },
            "淘宝": {
                "scraper_class": TaoBaoScraper,
                "config_class": TaoBaoConfig,
                "redis_config": {
                    'host': '8.155.9.53',
                    'port': 6300,
                    'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
                    'task_key': ':1:自动获取产品列表'
                }
            },
            "抖音": {
                "scraper_class": DYScraper,
                "config_class": DYConfig,
                "redis_config": {
                    'host': '8.155.9.53',
                    'port': 6300,
                    'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
                    'task_key': ':1:自动获取产品列表'
                }
            }
        }
        
        # 当前处理的任务信息
        self.current_task = None
        self.current_scraper = None
        
        # 设置日志
        self.setup_logging()
        
        # 创建GUI界面
        self.create_gui()
        
        # 初始化Redis连接
        self.init_redis_connections()
    
    def setup_logging(self):
        """设置日志"""
        self.logger = logging.getLogger('MultiPlatformMonitor')
        self.logger.setLevel(logging.INFO)
        
        # 确保输出目录存在
        os.makedirs('logs', exist_ok=True)
        
        # 文件处理器
        log_file = f'logs/gui_monitor_{datetime.now().strftime("%Y%m%d")}.log'
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        file_handler.setFormatter(logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        ))
        
        self.logger.addHandler(file_handler)
    
    def create_gui(self):
        """创建GUI界面（现代化设计）"""
        # 设置窗口背景色
        self.root.configure(bg='#f5f7fa')
        
        # 主框架
        main_frame = tk.Frame(self.root, bg='#f5f7fa', padx=15, pady=15)
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(2, weight=1)
        
        # 顶部标题栏（渐变效果）
        title_bar = tk.Frame(main_frame, bg='#667eea', height=80)
        title_bar.grid(row=0, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
        title_bar.grid_propagate(False)
        
        # 标题内容
        title_content = tk.Frame(title_bar, bg='#667eea')
        title_content.place(relx=0.5, rely=0.5, anchor='center')
        
        title_icon = tk.Label(title_content, text="🚀", font=('Arial', 28),
                             bg='#667eea', fg='white')
        title_icon.pack(side=tk.LEFT, padx=(0, 15))
        
        title_text_frame = tk.Frame(title_content, bg='#667eea')
        title_text_frame.pack(side=tk.LEFT)
        
        title_label = tk.Label(title_text_frame, text="水滴多平台采集系统", 
                              font=('Microsoft YaHei UI', 20, 'bold'),
                              bg='#667eea', fg='white')
        title_label.pack()
        
        subtitle_label = tk.Label(title_text_frame, text="Multi-Platform Scraper Monitor", 
                                 font=('Arial', 10),
                                 bg='#667eea', fg='#e0e7ff')
        subtitle_label.pack()
        
        # 左侧控制面板（现代化卡片）
        control_card = tk.Frame(main_frame, bg='white', relief=tk.FLAT, bd=0)
        control_card.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10))
        
        # 控制面板标题
        control_title = tk.Frame(control_card, bg='#667eea', height=45)
        control_title.pack(fill=tk.X)
        control_title.pack_propagate(False)
        
        tk.Label(control_title, text="⚙️ 控制中心", 
                font=('Microsoft YaHei UI', 13, 'bold'),
                bg='#667eea', fg='white').pack(pady=10)
        
        # 控制面板内容
        control_content = tk.Frame(control_card, bg='white', padx=20, pady=20)
        control_content.pack(fill=tk.BOTH, expand=True)
        
        # 监听状态（美化）
        status_container = tk.Frame(control_content, bg='#f8f9fa', relief=tk.FLAT)
        status_container.pack(fill=tk.X, pady=(0, 15))
        
        status_inner = tk.Frame(status_container, bg='#f8f9fa', padx=15, pady=12)
        status_inner.pack(fill=tk.X)
        
        status_label = tk.Label(status_inner, text="监听状态", 
                               font=('Microsoft YaHei UI', 10),
                               bg='#f8f9fa', fg='#555')
        status_label.pack(side=tk.LEFT)
        
        self.status_var = tk.StringVar(value="未启动")
        self.status_display = tk.Label(status_inner, textvariable=self.status_var, 
                                      font=('Microsoft YaHei UI', 10, 'bold'),
                                      bg='#f8f9fa', fg='#e74c3c')
        self.status_display.pack(side=tk.RIGHT)
        
        # 当前任务（美化）
        task_container = tk.Frame(control_content, bg='#f8f9fa', relief=tk.FLAT)
        task_container.pack(fill=tk.X, pady=(0, 20))
        
        task_inner = tk.Frame(task_container, bg='#f8f9fa', padx=15, pady=12)
        task_inner.pack(fill=tk.X)
        
        task_label = tk.Label(task_inner, text="当前任务", 
                             font=('Microsoft YaHei UI', 10),
                             bg='#f8f9fa', fg='#555')
        task_label.pack(anchor=tk.W)
        
        self.current_task_var = tk.StringVar(value="无")
        self.current_task_display = tk.Label(task_inner, textvariable=self.current_task_var,
                                            font=('Microsoft YaHei UI', 9),
                                            bg='#f8f9fa', fg='#666',
                                            wraplength=200)
        self.current_task_display.pack(anchor=tk.W, pady=(5, 0))
        
        # 控制按钮（现代化样式）
        button_container = tk.Frame(control_content, bg='white')
        button_container.pack(fill=tk.X, pady=(0, 15))
        
        self.start_btn = tk.Button(button_container, text="▶  开始监听", 
                                  command=self.start_monitoring,
                                  font=('Microsoft YaHei UI', 11, 'bold'),
                                  bg='#27ae60', fg='white',
                                  cursor='hand2', relief=tk.FLAT,
                                  height=2,
                                  activebackground='#229954')
        self.start_btn.pack(fill=tk.X, pady=(0, 8))
        
        self.stop_btn = tk.Button(button_container, text="⏸  停止监听", 
                                 command=self.stop_monitoring,
                                 font=('Microsoft YaHei UI', 11),
                                 bg='#e0e0e0', fg='#999',
                                 cursor='hand2', relief=tk.FLAT,
                                 height=2, state='disabled',
                                 activebackground='#d0d0d0')
        self.stop_btn.pack(fill=tk.X, pady=(0, 8))
        
        # 测试连接按钮
        test_btn = tk.Button(button_container, text="🔌 测试连接", 
                            command=self.test_connections,
                            font=('Microsoft YaHei UI', 10),
                            bg='#3498db', fg='white',
                            cursor='hand2', relief=tk.FLAT,
                            height=2,
                            activebackground='#2980b9')
        test_btn.pack(fill=tk.X)
        
        # 添加按钮悬停效果
        def create_hover_effect(button, normal_color, hover_color):
            def on_enter(e):
                if button['state'] != 'disabled':
                    button.config(bg=hover_color)
            def on_leave(e):
                if button['state'] != 'disabled':
                    button.config(bg=normal_color)
            button.bind('<Enter>', on_enter)
            button.bind('<Leave>', on_leave)
        
        create_hover_effect(self.start_btn, '#27ae60', '#229954')
        create_hover_effect(test_btn, '#3498db', '#2980b9')
        
        # 平台状态（现代化设计）
        platform_title = tk.Label(control_content, text="📡 平台状态", 
                                 font=('Microsoft YaHei UI', 11, 'bold'),
                                 bg='white', fg='#2c3e50')
        platform_title.pack(anchor=tk.W, pady=(10, 10))
        
        platform_container = tk.Frame(control_content, bg='white')
        platform_container.pack(fill=tk.X)
        
        self.platform_status = {}
        platform_icons = {
            "拼多多": "🛒",
            "京东": "🏪", 
            "淘宝": "🛍️",
            "抖音": "🎵"
        }
        
        for i, platform in enumerate(self.platform_configs.keys()):
            # 每个平台一个小卡片
            platform_card = tk.Frame(platform_container, bg='#f8f9fa', relief=tk.FLAT)
            platform_card.pack(fill=tk.X, pady=(0, 8))
            
            platform_inner = tk.Frame(platform_card, bg='#f8f9fa', padx=12, pady=10)
            platform_inner.pack(fill=tk.X)
            
            # 平台图标和名称
            icon = platform_icons.get(platform, "📦")
            platform_label = tk.Label(platform_inner, 
                                     text=f"{icon} {platform}", 
                                     font=('Microsoft YaHei UI', 10),
                                     bg='#f8f9fa', fg='#34495e')
            platform_label.pack(side=tk.LEFT)
            
            # 状态标签
            status_var = tk.StringVar(value="未检测")
            status_label = tk.Label(platform_inner, textvariable=status_var, 
                                   font=('Microsoft YaHei UI', 9),
                                   bg='#f8f9fa', fg='#95a5a6')
            status_label.pack(side=tk.RIGHT)
            
            self.platform_status[platform] = status_var
        
        # 右侧日志面板（现代化卡片）
        log_card = tk.Frame(main_frame, bg='white', relief=tk.FLAT)
        log_card.grid(row=1, column=1, rowspan=2, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 日志面板标题
        log_title_bar = tk.Frame(log_card, bg='#667eea', height=45)
        log_title_bar.pack(fill=tk.X)
        log_title_bar.pack_propagate(False)
        
        log_title_content = tk.Frame(log_title_bar, bg='#667eea')
        log_title_content.place(relx=0.5, rely=0.5, anchor='center')
        
        tk.Label(log_title_content, text="📋 运行日志", 
                font=('Microsoft YaHei UI', 13, 'bold'),
                bg='#667eea', fg='white').pack(side=tk.LEFT, padx=(0, 10))
        
        clear_btn = tk.Button(log_title_content, text="🗑️ 清空", 
                             command=self.clear_log,
                             font=('Microsoft YaHei UI', 9),
                             bg='#764ba2', fg='white',
                             cursor='hand2', relief=tk.FLAT,
                             padx=15, pady=3,
                             activebackground='#6a3f92')
        clear_btn.pack(side=tk.LEFT)
        
        # 日志内容区域
        log_content = tk.Frame(log_card, bg='white', padx=10, pady=10)
        log_content.pack(fill=tk.BOTH, expand=True)
        
        # 日志文本框（美化）
        self.log_text = scrolledtext.ScrolledText(log_content, wrap=tk.WORD, 
                                                 width=80, height=30,
                                                 font=('Consolas', 9),
                                                 bg='#1e1e1e', fg='#d4d4d4',
                                                 insertbackground='white',
                                                 selectbackground='#264f78',
                                                 relief=tk.FLAT, bd=0)
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        # 配置日志文本标签（彩色日志）
        self.log_text.tag_config('INFO', foreground='#4ec9b0')
        self.log_text.tag_config('WARNING', foreground='#dcdcaa')
        self.log_text.tag_config('ERROR', foreground='#f48771')
        self.log_text.tag_config('SUCCESS', foreground='#b5cea8')
        
        # 统计信息框架（现代化卡片）
        stats_card = tk.Frame(main_frame, bg='white', relief=tk.FLAT)
        stats_card.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10), pady=(10, 0))
        
        # 统计面板标题
        stats_title = tk.Frame(stats_card, bg='#667eea', height=45)
        stats_title.pack(fill=tk.X)
        stats_title.pack_propagate(False)
        
        tk.Label(stats_title, text="📊 统计中心", 
                font=('Microsoft YaHei UI', 13, 'bold'),
                bg='#667eea', fg='white').pack(pady=10)
        
        # 统计内容
        stats_content = tk.Frame(stats_card, bg='white', padx=20, pady=20)
        stats_content.pack(fill=tk.BOTH, expand=True)
        
        # 任务统计
        self.stats_vars = {
            'total_tasks': tk.StringVar(value="0"),
            'success_tasks': tk.StringVar(value="0"),
            'failed_tasks': tk.StringVar(value="0"),
            'current_platform': tk.StringVar(value="无")
        }
        
        stats_config = [
            ("📦 总任务数", 'total_tasks', '#3498db'),
            ("✅ 成功任务", 'success_tasks', '#27ae60'),
            ("❌ 失败任务", 'failed_tasks', '#e74c3c'),
            ("🎯 当前平台", 'current_platform', '#9b59b6')
        ]
        
        for i, (label_text, var_key, color) in enumerate(stats_config):
            # 每个统计项一个卡片
            stat_card = tk.Frame(stats_content, bg='#f8f9fa', relief=tk.FLAT)
            stat_card.pack(fill=tk.X, pady=(0, 10))
            
            stat_inner = tk.Frame(stat_card, bg='#f8f9fa', padx=15, pady=12)
            stat_inner.pack(fill=tk.X)
            
            # 标签
            label = tk.Label(stat_inner, text=label_text, 
                           font=('Microsoft YaHei UI', 10),
                           bg='#f8f9fa', fg='#555')
            label.pack(side=tk.LEFT)
            
            # 数值
            value_label = tk.Label(stat_inner, textvariable=self.stats_vars[var_key],
                                  font=('Microsoft YaHei UI', 12, 'bold'),
                                  bg='#f8f9fa', fg=color)
            value_label.pack(side=tk.RIGHT)
    
    def log_message(self, message: str, level: str = "INFO"):
        """添加日志消息到GUI（带彩色标签）"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        
        # 线程安全的GUI更新
        def update_log():
            # 插入时间戳
            self.log_text.insert(tk.END, f"[{timestamp}] ", 'INFO')
            
            # 插入级别标签（带颜色）
            if level == "ERROR":
                self.log_text.insert(tk.END, f"{level}: ", 'ERROR')
            elif level == "WARNING":
                self.log_text.insert(tk.END, f"{level}: ", 'WARNING')
            elif "✅" in message or "成功" in message:
                self.log_text.insert(tk.END, f"{level}: ", 'SUCCESS')
            else:
                self.log_text.insert(tk.END, f"{level}: ", 'INFO')
            
            # 插入消息内容
            self.log_text.insert(tk.END, f"{message}\n")
            self.log_text.see(tk.END)
        
        self.root.after(0, update_log)
        
        # 同时记录到文件日志
        if level == "ERROR":
            self.logger.error(message)
        elif level == "WARNING":
            self.logger.warning(message)
        else:
            self.logger.info(message)
    
    def clear_log(self):
        """清空日志"""
        self.log_text.delete(1.0, tk.END)
    
    def init_redis_connections(self):
        """初始化Redis连接"""
        self.log_message("初始化Redis连接...")
        
        for platform, config in self.platform_configs.items():
            try:
                redis_config = config['redis_config']
                client = redis.Redis(
                    host=redis_config['host'],
                    port=redis_config['port'],
                    password=redis_config['password'],
                    decode_responses=False,
                    # 添加连接超时配置
                    socket_connect_timeout=5,  # 连接超时5秒
                    socket_timeout=10,         # 读写超时10秒
                    # 连接池配置
                    connection_pool=redis.ConnectionPool(
                        host=redis_config['host'],
                        port=redis_config['port'],
                        password=redis_config['password'],
                        decode_responses=False,
                        socket_connect_timeout=5,
                        socket_timeout=10,
                        max_connections=10,    # 最大连接数
                        retry_on_timeout=True, # 超时重试
                        health_check_interval=30  # 每30秒健康检查
                    )
                )
                
                # 测试连接
                client.ping()
                self.redis_clients[platform] = client
                self.platform_status[platform].set("连接正常")
                self.log_message(f"{platform} Redis连接成功")
                
            except Exception as e:
                self.platform_status[platform].set("连接失败")
                self.log_message(f"{platform} Redis连接失败: {e}", "ERROR")
    
    def test_connections(self):
        """测试所有连接（异步执行，避免阻塞GUI）"""
        # 禁用测试按钮，防止重复点击
        for widget in self.root.winfo_children():
            if hasattr(widget, 'winfo_children'):
                for child in widget.winfo_children():
                    if hasattr(child, 'winfo_children'):
                        for button in child.winfo_children():
                            if isinstance(button, ttk.Button) and button.cget('text') == '测试连接':
                                button.config(state='disabled', text='测试中...')
                                break
        
        self.log_message("开始测试连接...")
        
        # 检查是否有连接可能需要重建
        inactive_connections = []
        for platform in self.platform_configs.keys():
            if platform not in self.redis_clients:
                inactive_connections.append(platform)
        
        if inactive_connections:
            self.log_message(f"ℹ️ 检测到 {len(inactive_connections)} 个平台需要重新建立连接，首次测试可能需要较长时间...")
        
        # 在后台线程中执行测试
        def async_test():
            try:
                platforms = list(self.platform_configs.keys())
                total_platforms = len(platforms)
                
                for index, platform in enumerate(platforms, 1):
                    # 更新进度
                    self.root.after(0, lambda p=platform, i=index, t=total_platforms: 
                                   self.log_message(f"🔍 测试 {platform} 连接... ({i}/{t})"))
                    
                    try:
                        if platform in self.redis_clients:
                            # 尝试ping测试
                            try:
                                self.redis_clients[platform].ping()
                                self.root.after(0, lambda p=platform: (
                                    self.platform_status[p].set("连接正常"),
                                    self.log_message(f"{p} 连接测试通过")
                                ))
                            except (redis.TimeoutError, redis.ConnectionError) as e:
                                # 连接超时或断开，尝试重连
                                self.root.after(0, lambda p=platform, err=e: 
                                               self.log_message(f"{p} 连接异常，尝试重连: {err}", "WARNING"))
                                self._reconnect_platform_sync(platform)
                                
                        else:
                            self.root.after(0, lambda p=platform: 
                                           self.log_message(f"{p} 未建立连接，尝试重连...", "WARNING"))
                            self._reconnect_platform_sync(platform)
                            
                    except Exception as e:
                        self.root.after(0, lambda p=platform, err=e: (
                            self.platform_status[p].set("连接失败"),
                            self.log_message(f"{p} 连接测试失败: {err}", "ERROR")
                        ))
                
                # 测试完成，恢复按钮状态
                def restore_button():
                    self.log_message("连接测试完成")
                    for widget in self.root.winfo_children():
                        if hasattr(widget, 'winfo_children'):
                            for child in widget.winfo_children():
                                if hasattr(child, 'winfo_children'):
                                    for button in child.winfo_children():
                                        if isinstance(button, ttk.Button) and button.cget('text') == '测试中...':
                                            button.config(state='normal', text='测试连接')
                                            break
                
                self.root.after(0, restore_button)
                
            except Exception as e:
                self.root.after(0, lambda: self.log_message(f"测试连接异常: {e}", "ERROR"))
                # 确保按钮状态恢复
                def restore_button_on_error():
                    for widget in self.root.winfo_children():
                        if hasattr(widget, 'winfo_children'):
                            for child in widget.winfo_children():
                                if hasattr(child, 'winfo_children'):
                                    for button in child.winfo_children():
                                        if isinstance(button, ttk.Button) and button.cget('text') == '测试中...':
                                            button.config(state='normal', text='测试连接')
                                            break
                self.root.after(0, restore_button_on_error)
        
        # 启动后台线程
        import threading
        test_thread = threading.Thread(target=async_test, daemon=True)
        test_thread.start()
    
    def _reconnect_platform_sync(self, platform: str):
        """同步版本的重连方法（用于后台线程）"""
        try:
            config = self.platform_configs[platform]
            redis_config = config['redis_config']
            
            # 关闭旧连接
            if platform in self.redis_clients:
                try:
                    self.redis_clients[platform].close()
                except:
                    pass
                del self.redis_clients[platform]
            
            # 创建新连接
            client = redis.Redis(
                host=redis_config['host'],
                port=redis_config['port'],
                password=redis_config['password'],
                decode_responses=False,
                socket_connect_timeout=5,
                socket_timeout=10,
                connection_pool=redis.ConnectionPool(
                    host=redis_config['host'],
                    port=redis_config['port'],
                    password=redis_config['password'],
                    decode_responses=False,
                    socket_connect_timeout=5,
                    socket_timeout=10,
                    max_connections=10,
                    retry_on_timeout=True,
                    health_check_interval=30
                )
            )
            
            # 测试新连接
            client.ping()
            self.redis_clients[platform] = client
            
            # 在主线程中更新UI
            self.root.after(0, lambda: (
                self.platform_status[platform].set("连接正常"),
                self.log_message(f"✅ {platform} 重连成功")
            ))
            
        except Exception as e:
            # 在主线程中更新UI
            self.root.after(0, lambda: (
                self.platform_status[platform].set("连接失败"),
                self.log_message(f"❌ {platform} 重连失败: {e}", "ERROR")
            ))
    
    def _periodic_health_check(self):
        """定期Redis连接健康检查"""
        try:
            for platform in self.platform_configs.keys():
                if platform in self.redis_clients:
                    try:
                        # 设置较短的超时时间进行快速检查
                        client = self.redis_clients[platform]
                        client.ping()
                        
                    except (redis.TimeoutError, redis.ConnectionError, redis.ResponseError) as e:
                        self.log_message(f"⚠️ {platform} Redis连接异常，自动重连: {e}", "WARNING")
                        self._reconnect_platform(platform)
                        
                    except Exception as e:
                        self.log_message(f"⚠️ {platform} 健康检查异常: {e}", "WARNING")
                        
            self.log_message("✅ Redis连接健康检查完成")
            
        except Exception as e:
            self.log_message(f"⚠️ 定期健康检查异常: {e}", "WARNING")

    def _reconnect_platform(self, platform: str):
        """重连指定平台的Redis"""
        try:
            config = self.platform_configs[platform]
            redis_config = config['redis_config']
            
            # 关闭旧连接
            if platform in self.redis_clients:
                try:
                    self.redis_clients[platform].close()
                except:
                    pass
                del self.redis_clients[platform]
            
            # 创建新连接
            client = redis.Redis(
                host=redis_config['host'],
                port=redis_config['port'],
                password=redis_config['password'],
                decode_responses=False,
                socket_connect_timeout=5,
                socket_timeout=10,
                connection_pool=redis.ConnectionPool(
                    host=redis_config['host'],
                    port=redis_config['port'],
                    password=redis_config['password'],
                    decode_responses=False,
                    socket_connect_timeout=5,
                    socket_timeout=10,
                    max_connections=10,
                    retry_on_timeout=True,
                    health_check_interval=30
                )
            )
            
            # 测试新连接
            client.ping()
            self.redis_clients[platform] = client
            self.platform_status[platform].set("连接正常")
            self.log_message(f"✅ {platform} 重连成功")
            
        except Exception as e:
            self.platform_status[platform].set("连接失败")
            self.log_message(f"❌ {platform} 重连失败: {e}", "ERROR")
    
    def get_platform_tasks(self, platform: str) -> List[Dict[str, Any]]:
        """获取指定平台的任务（只获取当前令牌的任务）"""
        try:
            if platform not in self.redis_clients:
                return []
            
            client = self.redis_clients[platform]
            config = self.platform_configs[platform]
            task_key = config['redis_config']['task_key']
            
            try:
                task_data = client.get(task_key)
            except (redis.TimeoutError, redis.ConnectionError) as e:
                # 连接异常，尝试重连后再次获取
                self.log_message(f"⚠️ {platform} 获取任务时连接异常，尝试重连: {e}", "WARNING")
                self._reconnect_platform(platform)
                
                # 重连后再次尝试
                if platform in self.redis_clients:
                    try:
                        client = self.redis_clients[platform]
                        task_data = client.get(task_key)
                    except Exception as retry_e:
                        self.log_message(f"❌ {platform} 重连后仍无法获取任务: {retry_e}", "ERROR")
                        return []
                else:
                    return []
            
            if not task_data:
                return []
            
            # 尝试pickle反序列化
            try:
                tasks = pickle.loads(task_data)
                
                # 如果pickle返回的是字符串（常见情况）
                if isinstance(tasks, str):
                    try:
                        tasks = json.loads(tasks)
                    except json.JSONDecodeError as e:
                        self.log_message(f"JSON解析失败: {e}", "ERROR")
                        return []
                
                # 新格式：{token: [tasks]} 字典结构
                if isinstance(tasks, dict):
                    # 获取当前令牌对应的任务列表
                    user_tasks = tasks.get(self.user_token, [])
                    
                    if not isinstance(user_tasks, list):
                        self.log_message(f"⚠️ 令牌 {self.user_token} 对应的任务格式错误", "WARNING")
                        return []
                    
                            # 只返回status为false的未完成任务
                    filtered_tasks = [task for task in user_tasks if task.get('status') == False]
                    return filtered_tasks
                
                # 旧格式：[tasks] 列表结构（兼容）
                elif isinstance(tasks, list):
                    # 只返回status为false的未完成任务
                    filtered_tasks = [task for task in tasks if task.get('status') == False]
                    return filtered_tasks
                    
            except Exception as e:
                self.log_message(f"Pickle反序列化失败: {e}", "ERROR")
            
            # 尝试JSON反序列化
            try:
                task_str = task_data.decode('utf-8')
                tasks = json.loads(task_str)
                if isinstance(tasks, list):
                    # 只返回status为false的未完成任务
                    filtered_tasks = [task for task in tasks if task.get('status') == False]
                    return filtered_tasks
                elif isinstance(tasks, dict):
                    # 单个任务，检查status
                    if tasks.get('status') == False:
                        return [tasks]
                    else:
                        return []  # 已完成任务，不返回
            except:
                pass
            
            return []
            
        except Exception as e:
            self.log_message(f"获取{platform}任务失败: {e}", "ERROR")
            return []
    
    def get_all_tasks_including_completed(self, platform: str) -> List[Dict[str, Any]]:
        """获取指定平台的所有任务（包括已完成的，只返回当前令牌的）"""
        try:
            if platform not in self.redis_clients:
                return []
            
            client = self.redis_clients[platform]
            config = self.platform_configs[platform]
            task_key = config['redis_config']['task_key']
            
            task_data = client.get(task_key)
            if not task_data:
                return []
            
            # 尝试pickle反序列化
            try:
                tasks = pickle.loads(task_data)
                
                # 如果pickle返回的是字符串
                if isinstance(tasks, str):
                    try:
                        tasks = json.loads(tasks)
                    except json.JSONDecodeError as e:
                        self.log_message(f"JSON解析失败: {e}", "ERROR")
                        return []
                
                # 新格式：{token: [tasks]} 字典结构
                if isinstance(tasks, dict):
                    user_tasks = tasks.get(self.user_token, [])
                    return user_tasks if isinstance(user_tasks, list) else []
                
                # 旧格式：[tasks] 列表结构（兼容）
                elif isinstance(tasks, list):
                    return tasks
                    
            except Exception as e:
                self.log_message(f"Pickle反序列化失败: {e}", "ERROR")
            
            # 尝试JSON反序列化
            try:
                task_str = task_data.decode('utf-8')
                tasks = json.loads(task_str)
                if isinstance(tasks, list):
                    return tasks  # 返回所有任务，不过滤status
                elif isinstance(tasks, dict):
                    return [tasks]
            except:
                pass
            
            return []
            
        except Exception as e:
            self.log_message(f"获取{platform}所有任务失败: {e}", "ERROR")
            return []

    def update_platform_tasks(self, platform: str, tasks: List[Dict[str, Any]]) -> bool:
        """更新平台任务（只更新当前令牌的任务）"""
        try:
            if platform not in self.redis_clients:
                return False
            
            client = self.redis_clients[platform]
            config = self.platform_configs[platform]
            task_key = config['redis_config']['task_key']
            
            # 获取Redis中的所有数据
            task_data = client.get(task_key)
            
            # 解析现有数据
            all_data = {}
            if task_data:
                try:
                    all_data = pickle.loads(task_data)
                    
                    # 如果是字符串，尝试JSON解析
                    if isinstance(all_data, str):
                        all_data = json.loads(all_data)
                    
                    # 如果是列表（旧格式），转换为新格式
                    if isinstance(all_data, list):
                        # 保存旧数据到当前令牌下
                        all_data = {self.user_token: all_data}
                    
                    # 确保是字典格式
                    if not isinstance(all_data, dict):
                        all_data = {}
                        
                except Exception as e:
                    self.log_message(f"⚠️ 解析现有数据失败: {e}，将创建新数据结构", "WARNING")
                    all_data = {}
            
            # 更新当前令牌的任务
            all_data[self.user_token] = tasks
            
            # 序列化并保存
            # 先转JSON字符串，再pickle（保持与Django一致）
            json_str = json.dumps(all_data, ensure_ascii=False, default=str)
            pickle_data = pickle.dumps(json_str)
            client.set(task_key, pickle_data)
            
            return True
            
        except Exception as e:
            self.log_message(f"更新{platform}任务失败: {e}", "ERROR")
            return False
    
    def update_all_tasks(self, platform: str, tasks: List[Dict[str, Any]]) -> bool:
        """更新平台所有任务（包括已完成的）"""
        return self.update_platform_tasks(platform, tasks)
    
    def _update_task_status(self, store_id: str, status: bool, platform: str):
        """
        更新单个任务的状态（优化版本）
        
        Args:
            store_id: 任务的store_id
            status: 新的状态（True/False）
            platform: 平台名称
        """
        try:
            # 获取第一个平台的Redis客户端（所有平台共用同一个key）
            first_platform = list(self.platform_configs.keys())[0]
            
            if first_platform not in self.redis_clients:
                self.log_message(f"⚠️ Redis连接不可用", "WARNING")
                return
            
            client = self.redis_clients[first_platform]
            config = self.platform_configs[first_platform]
            task_key = config['redis_config']['task_key']
            
            # 获取所有数据
            task_data = client.get(task_key)
            if not task_data:
                self.log_message(f"⚠️ Redis中无任务数据", "WARNING")
                return
            
            # 解析数据
            all_data = pickle.loads(task_data)
            if isinstance(all_data, str):
                all_data = json.loads(all_data)
            
            # 确保是字典格式
            if not isinstance(all_data, dict):
                self.log_message(f"⚠️ 数据格式错误，无法更新", "WARNING")
                return
            
            # 获取当前令牌的任务列表
            user_tasks = all_data.get(self.user_token, [])
            
            # 查找并更新任务
            task_updated = False
            for i, t in enumerate(user_tasks):
                if isinstance(t, dict) and t.get('store_id') == store_id:
                    user_tasks[i]['status'] = status
                    task_updated = True
                    break
            
            if task_updated:
                # 更新Redis
                all_data[self.user_token] = user_tasks
                json_str = json.dumps(all_data, ensure_ascii=False, default=str)
                pickle_data = pickle.dumps(json_str)
                client.set(task_key, pickle_data)
                
                self.log_message(f"✅ {platform}任务状态已更新为{'完成' if status else '待处理'}")
                
                # 统计剩余未完成任务
                pending_count = len([t for t in user_tasks if t.get('status') == False])
                self.log_message(f"📋 当前令牌剩余未完成任务: {pending_count}")
            else:
                self.log_message(f"⚠️ 未找到store_id={store_id}的任务", "WARNING")
                
        except Exception as e:
            self.log_message(f"⚠️ 更新任务状态异常: {e}", "WARNING")
            import traceback
            self.logger.error(f"更新任务状态详情: {traceback.format_exc()}")
    
    def _update_task_retry_count(self, store_id: str, retry_count: int):
        """
        更新任务的重试次数
        
        Args:
            store_id: 任务的store_id
            retry_count: 重试次数
        """
        try:
            first_platform = list(self.platform_configs.keys())[0]
            
            if first_platform not in self.redis_clients:
                return
            
            client = self.redis_clients[first_platform]
            config = self.platform_configs[first_platform]
            task_key = config['redis_config']['task_key']
            
            # 获取所有数据
            task_data = client.get(task_key)
            if not task_data:
                return
            
            # 解析数据
            all_data = pickle.loads(task_data)
            if isinstance(all_data, str):
                all_data = json.loads(all_data)
            
            if not isinstance(all_data, dict):
                return
            
            # 获取当前令牌的任务列表
            user_tasks = all_data.get(self.user_token, [])
            
            # 查找并更新重试次数
            for i, t in enumerate(user_tasks):
                if isinstance(t, dict) and t.get('store_id') == store_id:
                    user_tasks[i]['retry_count'] = retry_count
                    break
            
            # 更新Redis
            all_data[self.user_token] = user_tasks
            json_str = json.dumps(all_data, ensure_ascii=False, default=str)
            pickle_data = pickle.dumps(json_str)
            client.set(task_key, pickle_data)
                
        except Exception as e:
            self.log_message(f"⚠️ 更新重试次数异常: {e}", "WARNING")
    
    def validate_task(self, task: Dict[str, Any], platform: str) -> tuple:
        """验证任务数据"""
        errors = []
        
        # 检查必需字段
        if not task.get('platform'):
            errors.append("缺少platform字段")
        elif task['platform'] != platform:
            errors.append(f"平台不匹配，期望{platform}，实际{task['platform']}")
        
        if not task.get('store_id'):
            errors.append("缺少store_id字段")
        
        if not task.get('store_url'):
            errors.append("缺少store_url字段")
        
        return len(errors) == 0, errors
    
    def show_login_mode_dialog(self, platform: str) -> str:
        """显示登录模式选择对话框（现代化设计）"""
        dialog = tk.Toplevel(self.root)
        dialog.title(f"{platform} 登录方式")
        dialog.geometry("540x420")
        dialog.resizable(False, False)
        dialog.configure(bg='#f5f7fa')
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 居中显示
        dialog.geometry("+%d+%d" % (
            self.root.winfo_rootx() + 100,
            self.root.winfo_rooty() + 100
        ))
        
        result = {'mode': None}
        
        # 顶部装饰条
        top_bar = tk.Frame(dialog, bg='#667eea', height=6)
        top_bar.pack(fill=tk.X)
        
        # 主卡片
        card = tk.Frame(dialog, bg='white')
        card.pack(fill=tk.BOTH, expand=True, padx=30, pady=(20, 15))
        
        # 标题区域
        title_container = tk.Frame(card, bg='white')
        title_container.pack(fill=tk.X, pady=(10, 8))
        
        platform_icons = {"拼多多": "🛒", "京东": "🏪", "淘宝": "🛍️", "抖音": "🎵"}
        icon = platform_icons.get(platform, "🔐")
        
        title_label = tk.Label(title_container, text=f"{icon} {platform} 登录方式", 
                              font=('Microsoft YaHei UI', 16, 'bold'),
                              bg='white', fg='#2c3e50')
        title_label.pack()
        
        desc_label = tk.Label(title_container, text="请选择适合您的登录方式", 
                             font=('Microsoft YaHei UI', 10),
                             bg='white', fg='#7f8c8d')
        desc_label.pack(pady=(5, 0))
        
        # 选项区域
        options_frame = tk.Frame(card, bg='white')
        options_frame.pack(fill=tk.X, pady=(15, 10), padx=15)
        
        mode_var = tk.StringVar(value="interactive")
        
        # 交互式登录选项（卡片样式）
        interactive_card = tk.Frame(options_frame, bg='#e8f4f8', relief=tk.FLAT, cursor='hand2')
        interactive_card.pack(fill=tk.X, pady=(0, 12))
        
        interactive_inner = tk.Frame(interactive_card, bg='#e8f4f8', padx=15, pady=12)
        interactive_inner.pack(fill=tk.X)
        
        interactive_radio = tk.Radiobutton(
            interactive_inner, 
            text="🤖 交互式登录",
            variable=mode_var, value="interactive",
            font=('Microsoft YaHei UI', 11, 'bold'),
            bg='#e8f4f8', fg='#2c3e50',
            activebackground='#e8f4f8',
            selectcolor='#e8f4f8',
            cursor='hand2'
        )
        interactive_radio.pack(anchor=tk.W)
        
        interactive_desc = tk.Label(interactive_inner, 
                                   text="程序协助填写，您只需处理验证码",
                                   font=('Microsoft YaHei UI', 9),
                                   bg='#e8f4f8', fg='#666')
        interactive_desc.pack(anchor=tk.W, padx=(25, 0), pady=(3, 0))
        
        # 手动登录选项（卡片样式）
        manual_card = tk.Frame(options_frame, bg='#fff3e0', relief=tk.FLAT, cursor='hand2')
        manual_card.pack(fill=tk.X)
        
        manual_inner = tk.Frame(manual_card, bg='#fff3e0', padx=15, pady=12)
        manual_inner.pack(fill=tk.X)
        
        manual_radio = tk.Radiobutton(
            manual_inner,
            text="✋ 手动登录",
            variable=mode_var, value="manual",
            font=('Microsoft YaHei UI', 11, 'bold'),
            bg='#fff3e0', fg='#2c3e50',
            activebackground='#fff3e0',
            selectcolor='#fff3e0',
            cursor='hand2'
        )
        manual_radio.pack(anchor=tk.W)
        
        manual_desc = tk.Label(manual_inner, 
                              text="完全手动操作，适合复杂验证场景",
                              font=('Microsoft YaHei UI', 9),
                              bg='#fff3e0', fg='#666')
        manual_desc.pack(anchor=tk.W, padx=(25, 0), pady=(3, 0))
        
        # 卡片点击效果
        def select_interactive(e):
            mode_var.set("interactive")
        def select_manual(e):
            mode_var.set("manual")
        
        interactive_card.bind('<Button-1>', select_interactive)
        manual_card.bind('<Button-1>', select_manual)
        for child in interactive_inner.winfo_children():
            child.bind('<Button-1>', select_interactive)
        for child in manual_inner.winfo_children():
            child.bind('<Button-1>', select_manual)
        
        # 按钮框架（固定在底部）
        button_frame = tk.Frame(card, bg='white')
        button_frame.pack(side=tk.BOTTOM, pady=(10, 15))
        
        def on_confirm():
            result['mode'] = mode_var.get()
            dialog.destroy()
        
        def on_cancel():
            result['mode'] = None
            dialog.destroy()
        
        confirm_btn = tk.Button(button_frame, text="确定", command=on_confirm,
                               width=16, height=2,
                               font=('Microsoft YaHei UI', 10, 'bold'),
                               bg='#667eea', fg='white',
                               cursor='hand2', relief=tk.RAISED, bd=1,
                               activebackground='#5568d3')
        confirm_btn.pack(side=tk.LEFT, padx=10)
        
        cancel_btn = tk.Button(button_frame, text="取消", command=on_cancel,
                              width=16, height=2,
                              font=('Microsoft YaHei UI', 10),
                              bg='#e0e0e0', fg='#333',
                              cursor='hand2', relief=tk.RAISED, bd=1,
                              activebackground='#d0d0d0')
        cancel_btn.pack(side=tk.LEFT, padx=10)
        
        # 等待对话框关闭
        dialog.wait_window()
        
        return result['mode']
    
    def show_login_confirmation_dialog(self, platform: str) -> bool:
        """显示登录确认对话框（线程安全）"""
        import queue
        result_queue = queue.Queue()
        
        def show_dialog():
            message = f"{platform} 手动登录\n\n"
            message += "请在浏览器中完成登录操作，然后点击确认。\n"
            message += "登录成功后通常会跳转到相应的管理页面。"
            
            confirmed = messagebox.askyesno("登录确认", message, parent=self.root)
            result_queue.put(confirmed)
        
        # 如果在主线程中，直接调用
        if threading.current_thread() == threading.main_thread():
            show_dialog()
        else:
            # 如果在子线程中，切换到主线程执行
            self.root.after(0, show_dialog)
        
        # 等待结果
        try:
            return result_queue.get(timeout=300)  # 5分钟超时
        except queue.Empty:
            return False  # 超时返回False
    
    def show_verification_dialog(self, platform: str, product_name: str = "") -> str:
        """显示验证码处理对话框（线程安全）"""
        import queue
        result_queue = queue.Queue()
        
        def create_dialog():
            dialog = tk.Toplevel(self.root)
            dialog.title(f"{platform} 验证码")
            dialog.geometry("550x480")
            dialog.resizable(False, False)
            dialog.configure(bg='#fff5f5')
            dialog.transient(self.root)
            dialog.grab_set()
            
            # 居中显示
            dialog.geometry("+%d+%d" % (
                self.root.winfo_rootx() + 100,
                self.root.winfo_rooty() + 80
            ))
            
            # 顶部装饰条（警告色）
            top_bar = tk.Frame(dialog, bg='#f39c12', height=6)
            top_bar.pack(fill=tk.X)
            
            # 主卡片
            card = tk.Frame(dialog, bg='white')
            card.pack(fill=tk.BOTH, expand=True, padx=30, pady=(20, 15))
            
            # 警告图标
            icon_canvas = tk.Canvas(card, width=60, height=60, bg='white', highlightthickness=0)
            icon_canvas.pack(pady=(10, 8))
            icon_canvas.create_oval(5, 5, 55, 55, fill='#fff3cd', outline='#f39c12', width=3)
            icon_canvas.create_text(30, 30, text='⚠️', font=('Arial', 24))
            
            # 标题
            title_label = tk.Label(card, text=f"{platform} 验证码检测", 
                                  font=('Microsoft YaHei UI', 14, 'bold'),
                                  bg='white', fg='#e67e22')
            title_label.pack(pady=(5, 3))
            
            # 商品信息
            if product_name:
                product_label = tk.Label(card, text=f"当前商品: {product_name[:30]}...", 
                                        font=('Microsoft YaHei UI', 9),
                                        bg='white', fg='#666')
                product_label.pack(pady=(0, 10))
            
            # 说明文本（美化）
            inst_frame = tk.Frame(card, bg='#fffbf0', relief=tk.FLAT)
            inst_frame.pack(fill=tk.X, padx=20, pady=(0, 12))
            
            inst_inner = tk.Frame(inst_frame, bg='#fffbf0', padx=15, pady=10)
            inst_inner.pack()
            
            inst_title = tk.Label(inst_inner, 
                                 text="请在浏览器中完成以下操作:",
                                 font=('Microsoft YaHei UI', 9, 'bold'),
                                 bg='#fffbf0', fg='#e67e22')
            inst_title.pack(anchor=tk.W, pady=(0, 6))
            
            instructions = [
                "✓ 完成滑动验证码",
                "✓ 完成点击验证码",
                "✓ 完成图形验证码",
                "✓ 等待页面正常加载"
            ]
            
            for inst in instructions:
                tk.Label(inst_inner, text=inst, 
                        font=('Microsoft YaHei UI', 8),
                        bg='#fffbf0', fg='#555').pack(anchor=tk.W, pady=1)
            
            # 操作提示
            action_label = tk.Label(card, text="处理完成后请选择操作:", 
                                   font=('Microsoft YaHei UI', 9),
                                   bg='white', fg='#666')
            action_label.pack(pady=(0, 10))
            
            # 按钮框架（固定在底部）
            button_frame = tk.Frame(card, bg='white')
            button_frame.pack(side=tk.BOTTOM, pady=(8, 15))
            
            def on_continue():
                result_queue.put('continue')
                dialog.destroy()
            
            def on_skip():
                result_queue.put('skip')
                dialog.destroy()
            
            def on_quit():
                result_queue.put('quit')
                dialog.destroy()
            
            continue_btn = tk.Button(button_frame, text="✓ 继续处理", 
                                    command=on_continue,
                                    width=12, height=2,
                                    font=('Microsoft YaHei UI', 9, 'bold'),
                                    bg='#27ae60', fg='white',
                                    cursor='hand2', relief=tk.RAISED, bd=1,
                                    activebackground='#229954')
            continue_btn.pack(side=tk.LEFT, padx=6)
            
            skip_btn = tk.Button(button_frame, text="⏭ 跳过当前", 
                                command=on_skip,
                                width=12, height=2,
                                font=('Microsoft YaHei UI', 9),
                                bg='#f39c12', fg='white',
                                cursor='hand2', relief=tk.RAISED, bd=1,
                                activebackground='#e67e22')
            skip_btn.pack(side=tk.LEFT, padx=6)
            
            quit_btn = tk.Button(button_frame, text="✕ 退出任务", 
                                command=on_quit,
                                width=12, height=2,
                                font=('Microsoft YaHei UI', 9),
                                bg='#e74c3c', fg='white',
                                cursor='hand2', relief=tk.RAISED, bd=1,
                                activebackground='#c0392b')
            quit_btn.pack(side=tk.LEFT, padx=6)
            
            # 等待对话框关闭
            dialog.wait_window()
        
        # 如果在主线程中，直接调用
        if threading.current_thread() == threading.main_thread():
            create_dialog()
        else:
            # 如果在子线程中，切换到主线程执行
            self.root.after(0, create_dialog)
        
        # 等待结果
        try:
            return result_queue.get(timeout=300)  # 5分钟超时
        except queue.Empty:
            return 'skip'  # 超时返回skip
    
    def show_credential_input_dialog(self, platform: str, sms_verification=False) -> tuple:
        """显示凭证输入对话框（线程安全）"""
        import queue
        result_queue = queue.Queue()
        
        def create_dialog():
            dialog = tk.Toplevel(self.root)
            if sms_verification:
                dialog.title(f"{platform} 短信验证码")
                dialog.geometry("480x260")
            else:
                dialog.title(f"{platform} 登录凭证")
                dialog.geometry("500x340")
            dialog.resizable(False, False)
            dialog.configure(bg='#f5f7fa')
            dialog.transient(self.root)
            dialog.grab_set()
            
            # 居中显示
            dialog.geometry("+%d+%d" % (
                self.root.winfo_rootx() + 100,
                self.root.winfo_rooty() + 100
            ))
            
            # 顶部装饰条
            top_bar = tk.Frame(dialog, bg='#667eea', height=6)
            top_bar.pack(fill=tk.X)
            
            # 主卡片
            card = tk.Frame(dialog, bg='white')
            card.pack(fill=tk.BOTH, expand=True, padx=30, pady=(20, 15))
            
            if sms_verification:
                # 短信验证码输入（美化）
                title_label = tk.Label(card, text=f"📱 {platform} 短信验证码", 
                                      font=('Microsoft YaHei UI', 16, 'bold'),
                                      bg='white', fg='#2c3e50')
                title_label.pack(pady=(20, 15))
                
                instruction_label = tk.Label(card, text="请输入收到的短信验证码:",
                                            font=('Microsoft YaHei UI', 10),
                                            bg='white', fg='#666')
                instruction_label.pack(pady=(0, 15))
                
                # 输入框容器
                entry_container = tk.Frame(card, bg='white', padx=40)
                entry_container.pack(fill=tk.X, pady=(0, 20))
                
                entry_frame = tk.Frame(entry_container, bg='#e8f0fe')
                entry_frame.pack(fill=tk.X)
                
                sms_entry = tk.Entry(entry_frame, font=('Consolas', 12),
                                    bg='#e8f0fe', fg='#2c3e50',
                                    relief=tk.FLAT, bd=0,
                                    insertbackground='#667eea')
                sms_entry.pack(fill=tk.X, padx=15, pady=10)
                sms_entry.focus()
                
                def on_confirm():
                    sms_code = sms_entry.get().strip()
                    if sms_code:
                        result_queue.put(sms_code)
                        dialog.destroy()
                    else:
                        messagebox.showerror("错误", "请输入验证码", parent=dialog)
                
                def on_cancel():
                    result_queue.put(None)
                    dialog.destroy()
            else:
                # 用户名密码输入（美化）
                title_label = tk.Label(card, text=f"🔐 {platform} 登录凭证", 
                                      font=('Microsoft YaHei UI', 16, 'bold'),
                                      bg='white', fg='#2c3e50')
                title_label.pack(pady=(20, 20))
                
                # 输入容器
                input_container = tk.Frame(card, bg='white', padx=40)
                input_container.pack(fill=tk.X)
                
                # 用户名
                username_label = tk.Label(input_container, text="用户名/手机号/邮箱:",
                                         font=('Microsoft YaHei UI', 10),
                                         bg='white', fg='#555')
                username_label.pack(anchor=tk.W, pady=(0, 6))
                
                username_frame = tk.Frame(input_container, bg='#e8f0fe')
                username_frame.pack(fill=tk.X, pady=(0, 15))
                
                username_entry = tk.Entry(username_frame, font=('Consolas', 10),
                                         bg='#e8f0fe', fg='#2c3e50',
                                         relief=tk.FLAT, bd=0,
                                         insertbackground='#667eea')
                username_entry.pack(fill=tk.X, padx=12, pady=8)
                username_entry.focus()
                
                # 密码
                password_label = tk.Label(input_container, text="密码:",
                                         font=('Microsoft YaHei UI', 10),
                                         bg='white', fg='#555')
                password_label.pack(anchor=tk.W, pady=(0, 6))
                
                password_frame = tk.Frame(input_container, bg='#e8f0fe')
                password_frame.pack(fill=tk.X, pady=(0, 20))
                
                password_entry = tk.Entry(password_frame, font=('Consolas', 10),
                                         bg='#e8f0fe', fg='#2c3e50',
                                         relief=tk.FLAT, bd=0,
                                         show="*",
                                         insertbackground='#667eea')
                password_entry.pack(fill=tk.X, padx=12, pady=8)
                
                def on_confirm():
                    username = username_entry.get().strip()
                    password = password_entry.get().strip()
                    if username and password:
                        result_queue.put((username, password))
                        dialog.destroy()
                    else:
                        messagebox.showerror("错误", "请输入用户名和密码", parent=dialog)
                
                def on_cancel():
                    result_queue.put(None)
                    dialog.destroy()
            
            # 按钮框架（固定在底部）
            button_frame = tk.Frame(card, bg='white')
            button_frame.pack(side=tk.BOTTOM, pady=(10, 20))
            
            confirm_btn = tk.Button(button_frame, text="确定", command=on_confirm,
                                   width=14, height=2,
                                   font=('Microsoft YaHei UI', 10, 'bold'),
                                   bg='#667eea', fg='white',
                                   cursor='hand2', relief=tk.RAISED, bd=1,
                                   activebackground='#5568d3')
            confirm_btn.pack(side=tk.LEFT, padx=10)
            
            cancel_btn = tk.Button(button_frame, text="取消", command=on_cancel,
                                  width=14, height=2,
                                  font=('Microsoft YaHei UI', 10),
                                  bg='#e0e0e0', fg='#333',
                                  cursor='hand2', relief=tk.RAISED, bd=1,
                                  activebackground='#d0d0d0')
            cancel_btn.pack(side=tk.LEFT, padx=10)
            
            # 绑定回车键
            if sms_verification:
                sms_entry.bind('<Return>', lambda e: on_confirm())
            else:
                password_entry.bind('<Return>', lambda e: on_confirm())
            
            # 等待对话框关闭
            dialog.wait_window()
        
        # 如果在主线程中，直接调用
        if threading.current_thread() == threading.main_thread():
            create_dialog()
        else:
            # 如果在子线程中，切换到主线程执行
            self.root.after(0, create_dialog)
        
        # 等待结果
        try:
            return result_queue.get(timeout=300)  # 5分钟超时
        except queue.Empty:
            return None  # 超时返回None
    
    def process_platform_task(self, platform: str, task: Dict[str, Any]) -> bool:
        """处理单个平台任务"""
        try:
            # 更新当前任务显示
            task_info = f"{platform} - {task.get('store_id', '未知')}"
            self.current_task_var.set(task_info)
            self.stats_vars['current_platform'].set(platform)
            
            self.log_message(f"🎯 开始处理{platform}任务: {task.get('store_id', '未知')}")
            
            # 获取平台配置
            config = self.platform_configs[platform]
            scraper_class = config['scraper_class']
            
            if not scraper_class:
                self.log_message(f"❌ {platform}爬虫模块未正确导入或安装", "ERROR")
                self.log_message(f"💡 请检查 {platform} 模块是否存在且可正常导入", "ERROR")
                return False
            
            # 初始化爬虫（显示浏览器以便用户登录）
            # 根据不同平台的__init__方法签名传递正确的参数
            if platform == "拼多多":
                # PDD scraper只接受headless参数
                scraper = scraper_class(headless=False)
            else:
                # 其他平台都支持headless和debug参数
                scraper = scraper_class(headless=False, debug=False)
            self.current_scraper = scraper
            
            try:
                # 初始化驱动 - 根据不同平台的初始化策略
                if platform in ["拼多多", "京东"]:
                    # PDD和JD在__init__中已经自动初始化了驱动，无需额外初始化
                    self.log_message(f"🔧 {platform}驱动已在初始化时自动设置")
                    # 验证驱动是否正常工作
                    if scraper.driver is None:
                        self.log_message(f"⚠️ {platform}驱动初始化异常，尝试手动初始化", "WARNING")
                        if hasattr(scraper, '_init_driver'):
                            scraper._init_driver()
                elif hasattr(scraper, '_init_driver'):
                    # DY平台使用_init_driver方法
                    scraper._init_driver()
                    self.log_message(f"🔧 {platform}驱动已通过_init_driver初始化")
                elif hasattr(scraper, 'init_driver'):
                    # TaoBao平台使用init_driver方法
                    scraper.init_driver()
                    self.log_message(f"🔧 {platform}驱动已通过init_driver初始化")
                else:
                    self.log_message(f"⚠️ {platform}平台驱动初始化方法未识别", "WARNING")
                
                self.log_message(f"🔧 {platform}爬虫已初始化")
                
                # 设置登录确认回调（如果支持）
                if hasattr(scraper, 'set_login_confirmation_callback'):
                    scraper.set_login_confirmation_callback(self.show_login_confirmation_dialog)
                
                # 设置验证码处理回调（如果支持）
                if hasattr(scraper, 'set_verification_callback'):
                    scraper.set_verification_callback(self.show_verification_dialog)
                
                # 设置凭证输入回调（如果支持）
                if hasattr(scraper, 'set_credential_input_callback'):
                    scraper.set_credential_input_callback(self.show_credential_input_dialog)
                
                # 登录流程
                login_mode = self.show_login_mode_dialog(platform)
                if not login_mode:
                    self.log_message(f"❌ 用户取消{platform}登录", "WARNING")
                    return False
                
                self.log_message(f"🔐 开始{platform}登录流程 (模式: {login_mode})")
                
                login_success = False
                if login_mode == "interactive":
                    # 交互式登录
                    if hasattr(scraper, 'login_jd'):
                        login_success = scraper.login_jd()
                    elif hasattr(scraper, 'login_taobao'):
                        login_success = scraper.login_taobao()
                    elif hasattr(scraper, 'login_dy'):
                        login_success = scraper.login_dy("interactive")
                    elif hasattr(scraper, 'login_pdd'):
                        login_success = scraper.login_pdd("interactive")
                else:
                    # 手动登录
                    if hasattr(scraper, 'login_jd'):
                        scraper.driver.get(config['config_class'].LOGIN_URL if config['config_class'] else "")
                        if self.show_login_confirmation_dialog(platform):
                            login_success = True
                    elif hasattr(scraper, 'login_taobao'):
                        scraper.driver.get("https://login.taobao.com/")
                        if self.show_login_confirmation_dialog(platform):
                            login_success = True
                    elif hasattr(scraper, 'login_dy'):
                        login_success = scraper.login_dy("manual")
                    elif hasattr(scraper, 'login_pdd'):
                        login_success = scraper.login_pdd("manual")
                
                if not login_success:
                    self.log_message(f"❌ {platform}登录失败", "ERROR")
                    return False
                
                self.log_message(f"✅ {platform}登录成功")
                
                # 导航到店铺页面 - 为PDD平台特殊处理
                store_url = task['store_url']
                
                if platform == "拼多多":
                    # PDD需要导航到商品列表页面，而不是具体的商品页面
                    self.log_message(f"🔧 {platform}导航到商品列表页面...")
                    if hasattr(scraper, 'navigate_to_goods_list'):
                        nav_success = scraper.navigate_to_goods_list()
                    else:
                        # 备用方案：直接导航到商品列表URL
                        scraper.driver.get("https://mms.pinduoduo.com/goods/goods_list")
                        import time
                        time.sleep(3)
                        nav_success = True
                    
                    if nav_success:
                        self.log_message(f"✅ {platform}成功导航到商品列表页面")
                    else:
                        self.log_message(f"❌ {platform}导航到商品列表页面失败", "ERROR")
                        return False
                else:
                    # 其他平台的原有逻辑
                    if hasattr(scraper, 'navigate_to_shop'):
                        nav_success = scraper.navigate_to_shop(store_url)
                    else:
                        scraper.driver.get(store_url)
                        nav_success = True
                    
                    if not nav_success:
                        self.log_message(f"❌ {platform}导航到店铺失败", "ERROR")
                        return False
                
                # 获取商品列表 - 根据不同平台的方法名适配
                self.log_message(f"📦 {platform}开始获取商品列表...")
                
                if platform == "拼多多":
                    # PDD使用get_all_products方法，但需要先确保页面状态正确
                    import time
                    
                    # 等待页面完全加载
                    self.log_message(f"⏳ {platform}等待商品列表页面完全加载...")
                    time.sleep(5)
                    
                    # 检查页面状态
                    try:
                        current_url = scraper.driver.current_url
                        self.log_message(f"🔍 {platform}当前页面URL: {current_url}")
                        
                        # 检查是否在商品列表页面
                        if 'goods_list' not in current_url:
                            self.log_message(f"⚠️ {platform}当前不在商品列表页面，尝试重新导航", "WARNING")
                            if hasattr(scraper, 'navigate_to_goods_list'):
                                nav_success = scraper.navigate_to_goods_list()
                                if not nav_success:
                                    self.log_message(f"❌ {platform}重新导航失败", "ERROR")
                                    return False
                                time.sleep(3)
                        
                        # 检查页面是否有商品表格
                        try:
                            scraper.driver.find_element(By.CSS_SELECTOR, "table.TB_tableWrapper_5-161-0")
                            self.log_message(f"✅ {platform}找到商品表格，页面状态正常")
                        except:
                            self.log_message(f"⚠️ {platform}未找到商品表格，可能页面未完全加载", "WARNING")
                            time.sleep(5)  # 再等待5秒
                            
                    except Exception as e:
                        self.log_message(f"⚠️ {platform}页面状态检查异常: {e}", "WARNING")
                    
                    # PDD使用get_all_products方法
                    products = scraper.get_all_products(max_pages=5, get_details=True)
                    self.log_message(f"🔧 {platform}使用get_all_products获取商品（已包含详情）")
                    
                    # 如果获取失败，尝试直接获取当前页面的商品
                    if not products:
                        self.log_message(f"⚠️ {platform}get_all_products未获取到商品，尝试直接提取当前页面", "WARNING")
                        try:
                            if hasattr(scraper, 'get_product_data_from_page'):
                                products = scraper.get_product_data_from_page()
                                if products:
                                    self.log_message(f"✅ {platform}通过直接提取获取到 {len(products)} 个商品")
                        except Exception as e:
                            self.log_message(f"❌ {platform}直接提取也失败: {e}", "ERROR")
                else:
                    # 其他平台使用get_product_list方法
                    if platform == "抖音":
                        # 抖音平台使用带类目提取的方法
                        self.log_message(f"🔍 {platform}使用增强版商品列表获取（包含类目信息）")
                        products = scraper.get_product_list_with_categories()
                        
                        # 添加类目信息统计
                        if products:
                            category_count = len([p for p in products if p.get('category')])
                            self.log_message(f"📊 {platform}类目提取统计: {category_count}/{len(products)} 个商品成功提取类目")
                    else:
                        # 其他平台使用标准方法
                        products = scraper.get_product_list()
                
                if not products:
                    self.log_message(f"⚠️ {platform}未获取到商品数据", "WARNING")
                    return True
                
                self.log_message(f"✅ {platform}获取到 {len(products)} 个商品")
                
                # 获取商品详情（带验证码处理） - PDD已在get_all_products中获取详情
                if platform == "拼多多":
                    # PDD的get_all_products已经包含了详情信息
                    self.log_message(f"🔍 {platform}商品详情已在列表获取时完成")
                    detailed_products = products
                else:
                    # 其他平台需要单独获取详情
                    self.log_message(f"🔍 {platform}开始获取商品详情...")
                    detailed_products = self.get_product_details_with_gui(scraper, products, platform)
                
                # 转换为API格式并提交
                self.log_message(f"🔄 {platform}转换数据格式...")
                api_products = []
                
                if platform == "拼多多":
                    # PDD的convert_to_api_format接受整个产品列表
                    api_products = scraper.convert_to_api_format(detailed_products)
                    self.log_message(f"🔧 {platform}使用批量转换API格式")
                else:
                    # 其他平台逐个转换
                    for product in detailed_products:
                        # 清理WebElement对象
                        clean_product = {k: v for k, v in product.items() if k != 'element'}
                        
                        # 检查是否已经是API格式（包含API格式的字段名）
                        if 'product_name' in clean_product and 'platform_product_id' in clean_product:
                            # 已经是API格式，直接使用
                            api_products.append(clean_product)
                        else:
                            # 需要转换为API格式
                            api_product = scraper.convert_to_api_format(clean_product)
                            if api_product:
                                api_products.append(api_product)
                                
                                # 为抖音平台添加product_type调试信息
                                if platform == "抖音":
                                    product_type = api_product.get('product_type', 'N/A')
                                    product_name = api_product.get('product_name', 'N/A')[:20]
                                    self.log_message(f"🎯 {platform}商品 '{product_name}...' product_type: {product_type}")
                
                # 提交到API前，保存和打印完整数据
                self.log_message(f"📤 {platform}提交数据到API: {len(api_products)} 个商品")
                
                # 构建简单的提交数据
                submit_data = {
                    'store_id': task['store_id'],
                    'platform': platform, 
                    'products': api_products
                }
                
                # 打印完整JSON数据到控制台
                self.log_message("=" * 80)
                self.log_message(f"📋 {platform}完整提交数据:")
                try:
                    import json
                    json_str = json.dumps(submit_data, ensure_ascii=False, separators=(',', ':'))
                    self.log_message(json_str)
                except Exception as e:
                    self.log_message(f"JSON序列化错误: {e}")
                    self.log_message(str(submit_data))
                self.log_message("=" * 80)
                
                # 保存到文件
                try:
                    import json
                    import time
                    import os
                    
                    # 创建输出目录
                    output_dir = "api_submit_data"
                    if not os.path.exists(output_dir):
                        os.makedirs(output_dir)
                    
                    # 生成文件名
                    timestamp = time.strftime('%Y%m%d_%H%M%S')
                    filename = f"{output_dir}/{platform}_{task['store_id'][:8]}_{timestamp}.json"
                    
                    # 保存JSON数据到文件（和控制台输出一样的格式）
                    with open(filename, 'w', encoding='utf-8') as f:
                        json.dump(submit_data, f, ensure_ascii=False, separators=(',', ':'))
                    
                    self.log_message(f"💾 数据已保存到文件: {filename}")
                    
                except Exception as e:
                    self.log_message(f"⚠️ 保存数据文件时出错: {e}", "WARNING")
                
                # 执行API提交
                self.log_message(f"📤 {platform}正在提交到API服务器...")
                
                api_success = scraper.submit_to_api(api_products, task['store_id'])
                
                if api_success:
                    # API提交成功，显示详细信息
                    self.log_message("=" * 60)
                    self.log_message(f"✅ API提交成功！", "INFO")
                    self.log_message(f"   平台: {platform}")
                    self.log_message(f"   店铺ID: {task['store_id']}")
                    self.log_message(f"   商品数量: {len(api_products)} 个")
                    self.log_message(f"   状态: 商品信息已成功添加/更新到系统")
                    self.log_message("=" * 60)
                    self.log_message(f"✅ {platform}任务处理完成")
                    return True
                else:
                    self.log_message(f"❌ {platform}API提交失败", "ERROR")
                    return False
                
            finally:
                # 清理资源
                try:
                    scraper.driver.quit()
                except:
                    pass
                self.current_scraper = None
        
        except Exception as e:
            self.log_message(f"❌ {platform}任务处理异常: {e}", "ERROR")
            return False
    
    def get_product_details_with_gui(self, scraper, products, platform):
        """获取商品详情（带GUI验证码处理）"""
        detailed_products = []
        
        for i, product in enumerate(products):
            try:
                self.log_message(f"📋 {platform}处理第 {i+1}/{len(products)} 个商品: {product.get('title', '未知')}")
                
                # 模拟验证码检测（这里需要根据实际爬虫的验证码检测逻辑来调整）
                verification_needed = self.check_verification_needed(scraper, platform)
                
                if verification_needed:
                    product_name = product.get('title', '未知商品')
                    action = self.show_verification_dialog(platform, product_name)
                    
                    if action == 'skip':
                        self.log_message(f"⏸️ {platform}跳过商品: {product_name}")
                        detailed_products.append(product)
                        continue
                    elif action == 'quit':
                        self.log_message(f"🛑 {platform}用户选择退出")
                        break
                    elif action == 'continue':
                        self.log_message(f"✅ {platform}继续处理商品: {product_name}")
                
                # 获取商品详情
                if hasattr(scraper, 'get_product_details'):
                    detail_products = scraper.get_product_details([product])
                    if detail_products:
                        detailed_products.extend(detail_products)
                    else:
                        detailed_products.append(product)
                
            except Exception as e:
                self.log_message(f"❌ {platform}处理商品详情异常: {e}", "ERROR")
                detailed_products.append(product)
        
        return detailed_products
    
    def check_verification_needed(self, scraper, platform) -> bool:
        """检查是否需要验证码处理（模拟）"""
        # 这里可以根据实际的验证码检测逻辑来实现
        # 目前只是模拟，可以根据平台的实际情况调整
        import random
        return random.random() < 0.1  # 10%概率出现验证码
    
    def _keep_gui_responsive(self):
        """保持GUI响应性（定期刷新）"""
        if self.is_monitoring:
            # 强制刷新GUI
            try:
                self.root.update_idletasks()
            except:
                pass
            # 每100ms刷新一次
            self.root.after(100, self._keep_gui_responsive)
    
    def monitoring_loop(self):
        """监听循环"""
        self.log_message("🎯 开始多平台任务监听")
        
        # 启动GUI响应性保持
        self.root.after(100, self._keep_gui_responsive)
        
        total_tasks = 0
        success_tasks = 0
        failed_tasks = 0
        last_health_check = 0  # 上次健康检查时间
        last_log_time = 0  # 上次日志时间
        
        try:
            while not self.stop_event.is_set():
                try:
                    # 在每次循环开始时立即检查停止信号
                    if self.stop_event.is_set():
                        self.log_message("🛑 监听器收到停止信号")
                        break
                    
                    # 定期进行Redis连接健康检查（每10分钟）
                    import time
                    current_time = time.time()
                    if current_time - last_health_check > 600:  # 10分钟 = 600秒
                        self.log_message("🔍 执行定期Redis连接健康检查...")
                        self._periodic_health_check()
                        last_health_check = current_time
                    
                    # 检查所有平台的任务（使用共享Redis key，根据任务中的platform字段分配）
                    all_tasks = []
                    
                    # 只从一个平台获取任务列表（因为都使用同一个Redis key）
                    first_platform = list(self.platform_configs.keys())[0]
                    tasks = self.get_platform_tasks(first_platform)
                    
                    if tasks:
                        # 减少日志输出，提高性能
                        current_log_time = time.time()
                        if current_log_time - last_log_time > 5:  # 每5秒最多记录一次
                            self.log_message(f"📋 Redis中发现 {len(tasks)} 个未完成任务，正在分配...")
                        last_log_time = current_log_time
                        
                        # 根据任务中的platform字段分配给对应平台
                        for task in tasks:
                            task_platform = task.get('platform', '').strip()
                            
                            # 映射任务平台名称到GUI平台名称
                            platform_mapping = {
                                '拼多多': '拼多多',
                                '京东': '京东', 
                                '淘宝': '淘宝',
                                '抖音': '抖音'
                            }
                            
                            target_platform = platform_mapping.get(task_platform)
                            
                            if target_platform and target_platform in self.platform_configs:
                                task['_platform'] = target_platform
                                all_tasks.append((target_platform, task))
                            else:
                                self.log_message(f"⚠️ 未知平台任务: {task_platform}，跳过", "WARNING")
                    else:
                        # 只在第一次循环时记录无任务信息
                        if total_tasks == 0:
                            self.log_message("📭 当前令牌暂无任务，等待中...")
                    
                    if not all_tasks:
                        # 使用快速轮询，提高响应性
                        # 使用可中断的短等待
                        for i in range(10):  # 总共2秒，每次0.2秒
                            if self.stop_event.is_set():
                                self.log_message("🛑 监听器收到停止信号")
                                return
                            time.sleep(0.2)
                        continue
                    
                    self.log_message(f"📋 发现 {len(all_tasks)} 个待处理任务")
                    
                    # 逐个处理任务
                    for platform, task in all_tasks:
                        if self.stop_event.is_set():
                            break
                        
                        total_tasks += 1
                        self.stats_vars['total_tasks'].set(str(total_tasks))
                        
                        try:
                            # 验证任务
                            is_valid, errors = self.validate_task(task, platform)
                            if not is_valid:
                                self.log_message(f"❌ {platform}任务数据无效: {'; '.join(errors)}", "ERROR")
                                failed_tasks += 1
                                self.stats_vars['failed_tasks'].set(str(failed_tasks))
                                continue
                            
                            # 处理任务
                            success = self.process_platform_task(platform, task)
                            
                            if success:
                                success_tasks += 1
                                self.stats_vars['success_tasks'].set(str(success_tasks))
                                self.log_message(f"✅ {platform}任务处理成功")
                                
                                # 将已完成任务的status改为true（优化版本）
                                try:
                                    self._update_task_status(task.get('store_id'), True, platform)
                                except Exception as e:
                                    self.log_message(f"⚠️ 更新Redis任务状态时出错: {e}，但任务已处理完成", "WARNING")
                                
                            else:
                                failed_tasks += 1
                                self.stats_vars['failed_tasks'].set(str(failed_tasks))
                                self.log_message(f"❌ {platform}任务处理失败", "ERROR")
                                
                                # 增加重试次数（优化版本）
                                try:
                                    task['retry_count'] = task.get('retry_count', 0) + 1
                                    if task['retry_count'] < 3:
                                        # 更新任务的重试次数（保持status=False）
                                        self._update_task_retry_count(task.get('store_id'), task['retry_count'])
                                        self.log_message(f"🔄 {platform}任务已更新重试次数: {task['retry_count']}/3")
                                    else:
                                        # 重试次数超限，标记为完成
                                        self.log_message(f"❌ {platform}任务重试次数超限，标记为完成", "ERROR")
                                        self._update_task_status(task.get('store_id'), True, platform)
                                            
                                except Exception as e:
                                    self.log_message(f"⚠️ 处理重试逻辑时出错: {e}", "WARNING")
                            
                        except Exception as e:
                            # 单个任务处理异常，不应中断整个监听循环
                            failed_tasks += 1
                            self.stats_vars['failed_tasks'].set(str(failed_tasks))
                            self.log_message(f"❌ 处理{platform}任务时发生异常: {e}", "ERROR")
                            self.log_message(f"📋 继续监听其他任务...", "INFO")
                            
                            # 记录详细异常信息到日志文件
                            import traceback
                            self.logger.error(f"任务处理异常详情: {traceback.format_exc()}")
                        
                        # 清空当前任务显示
                        self.current_task_var.set("无")
                        self.stats_vars['current_platform'].set("无")
                        
                        # 任务间隔（可中断的等待，优化为0.5秒）
                        if not self.stop_event.is_set():
                            for i in range(3):  # 分成3次，每次0.15秒，总共0.45秒
                                if self.stop_event.is_set():
                                    self.log_message("🛑 监听器收到停止信号")
                                    return
                                time.sleep(0.15)
                    
                    # 所有任务处理完成后，显示继续监听状态
                    self.log_message(f"🔄 当前批次任务处理完成，继续监听Redis队列...")
                    
                except Exception as e:
                    self.log_message(f"❌ 监听循环异常: {e}", "ERROR")
                    # 记录详细异常信息
                    import traceback
                    self.logger.error(f"监听循环异常详情: {traceback.format_exc()}")
                    
                    # 异常后的等待也要可中断
                    for i in range(50):  # 10秒，每次0.2秒
                        if self.stop_event.is_set():
                            self.log_message("🛑 监听器收到停止信号")
                            return
                        time.sleep(0.2)
        
        except Exception as e:
            self.log_message(f"❌ 监听器严重异常: {e}", "ERROR")
        
        finally:
            self.log_message("📴 任务监听已停止")
    
    def start_monitoring(self):
        """开始监听"""
        if self.is_monitoring:
            return
        
        self.is_monitoring = True
        self.stop_event.clear()
        
        # 更新UI状态（现代化样式）
        self.status_var.set("● 监听中")
        self.status_display.config(fg='#27ae60')
        self.start_btn.config(state='disabled', bg='#95a5a6', fg='#ccc')
        self.stop_btn.config(state='normal', bg='#e74c3c', fg='white')
        
        # 启动监听线程
        self.monitoring_thread = threading.Thread(target=self.monitoring_loop, daemon=True)
        self.monitoring_thread.start()
        
        self.log_message("🚀 多平台监听器已启动")
    
    def stop_monitoring(self):
        """停止监听"""
        if not self.is_monitoring:
            return
        
        self.log_message("🛑 正在停止监听器...")
        
        # 立即设置停止标志
        self.stop_event.set()
        self.is_monitoring = False
        
        # 立即更新UI状态
        self.status_var.set("◐ 正在停止...")
        self.status_display.config(fg='#f39c12')
        self.start_btn.config(state='disabled')
        self.stop_btn.config(state='disabled')
        
        # 使用异步方式处理资源清理，避免阻塞UI
        def async_cleanup():
            try:
                # 等待监听线程结束（短超时）
                if self.monitoring_thread and self.monitoring_thread.is_alive():
                    self.log_message("⏳ 等待监听线程结束...")
                    self.monitoring_thread.join(timeout=2)  # 缩短超时时间
                    
                    if self.monitoring_thread.is_alive():
                        self.log_message("⚠️ 监听线程未能及时结束，继续清理资源", "WARNING")
                
                # 异步清理浏览器资源
                self._cleanup_scraper_async()
                
                # 最终更新UI状态
                def update_final_status():
                    self.status_var.set("○ 已停止")
                    self.status_display.config(fg='#e74c3c')
                    self.start_btn.config(state='normal', bg='#27ae60', fg='white')
                    self.stop_btn.config(state='disabled', bg='#e0e0e0', fg='#999')
                    self.current_task_var.set("无")
                    self.stats_vars['current_platform'].set("无")
                    self.log_message("✅ 监听器已停止")
                
                # 在主线程中更新UI
                self.root.after(100, update_final_status)
                
            except Exception as e:
                self.log_message(f"⚠️ 停止监听器时出错: {e}", "WARNING")
                # 确保UI状态正确
                def fallback_update():
                    self.status_var.set("○ 已停止")
                    self.status_display.config(fg='#e74c3c')
                    self.start_btn.config(state='normal', bg='#27ae60', fg='white')
                    self.stop_btn.config(state='disabled', bg='#e0e0e0', fg='#999')
                
                self.root.after(100, fallback_update)
        
        # 在后台线程中执行清理操作
        cleanup_thread = threading.Thread(target=async_cleanup, daemon=True)
        cleanup_thread.start()
    
    def _cleanup_scraper_async(self):
        """异步清理爬虫资源"""
        if self.current_scraper:
            self.log_message("🧹 清理当前爬虫资源...")
            try:
                # 使用超时机制清理浏览器（跨平台方案）
                import time
                import threading
                
                cleanup_success = False
                
                def cleanup_browser():
                    nonlocal cleanup_success
                    try:
                        self.current_scraper.driver.quit()
                        cleanup_success = True
                        self.log_message("✅ 当前浏览器已关闭")
                    except Exception as e:
                        self.log_message(f"⚠️ 关闭浏览器时出错: {e}", "WARNING")
                
                # 在单独线程中执行清理
                cleanup_thread = threading.Thread(target=cleanup_browser, daemon=True)
                cleanup_thread.start()
                
                # 等待清理完成，最多等待3秒
                cleanup_thread.join(timeout=3)
                
                if cleanup_thread.is_alive():
                    self.log_message("⚠️ 浏览器关闭超时，强制清理", "WARNING")
                elif cleanup_success:
                    self.log_message("✅ 浏览器清理成功")
                
                self.current_scraper = None
                    
            except Exception as e:
                self.log_message(f"⚠️ 清理爬虫资源时出错: {e}", "WARNING")
                self.current_scraper = None
    
    def on_closing(self):
        """程序关闭处理"""
        # 检查是否正在处理任务（更精确的判断）
        is_scraping = (self.current_scraper is not None and 
                      self.current_task_var.get() != "无" and
                      self.is_monitoring)
        
        if is_scraping:
            # 正在爬取数据时，显示确认对话框
            if not messagebox.askyesno("确认关闭", 
                                     "当前正在处理爬虫任务，强制关闭可能导致数据丢失。\n\n确定要关闭程序吗？",
                                     icon='warning'):
                return  # 用户取消关闭
            
            self.log_message("⚠️ 用户选择强制关闭程序", "WARNING")
            self._force_close_program()
        else:
            # 空闲状态时，快速关闭
            self.log_message("🔄 程序正常关闭...")
            self._normal_close_program()
    
    def _normal_close_program(self):
        """正常关闭程序（空闲状态）"""
        try:
            # 停止监听
            if self.is_monitoring:
                self.log_message("🛑 停止监听器...")
                self.stop_event.set()
                self.is_monitoring = False
            
            # 快速清理资源（短超时）
            self._quick_cleanup()
            
            # 关闭程序
            self.log_message("👋 程序即将关闭")
            self.root.after(50, self.root.destroy)
            
        except Exception as e:
            self.log_message(f"⚠️ 正常关闭时出错: {e}", "WARNING")
            self.root.destroy()
    
    def _force_close_program(self):
        """强制关闭程序（爬取中状态）"""
        try:
            # 立即停止监听
            self.stop_event.set()
            self.is_monitoring = False
            
            # 强制清理当前爬虫（不等待）
            if self.current_scraper:
                self.log_message("🧹 强制清理浏览器资源...")
                try:
                    # 使用非阻塞方式关闭浏览器
                    def force_quit_browser():
                        try:
                            self.current_scraper.driver.quit()
                        except:
                            pass  # 忽略所有错误
                    
                    # 在后台线程中关闭浏览器
                    quit_thread = threading.Thread(target=force_quit_browser, daemon=True)
                    quit_thread.start()
                    
                    self.current_scraper = None
                    self.log_message("✅ 浏览器清理已启动")
                except Exception as e:
                    self.log_message(f"⚠️ 强制清理浏览器时出错: {e}", "WARNING")
            
            # 不等待线程结束，直接关闭
            self.log_message("👋 程序强制关闭")
            self.root.after(100, self.root.destroy)
            
        except Exception as e:
            self.log_message(f"⚠️ 强制关闭时出错: {e}", "WARNING")
            self.root.destroy()
    
    def _quick_cleanup(self):
        """快速清理资源"""
        try:
            # 等待监听线程结束（匹配实际等待时间)
            if self.monitoring_thread and self.monitoring_thread.is_alive():
                self.monitoring_thread.join(timeout=3)  # 增加到3秒，给线程足够时间响应
            
            # 清理爬虫资源（如果存在且不在使用中）
            if self.current_scraper and self.current_task_var.get() == "无":
                try:
                    self.current_scraper.driver.quit()
                    self.log_message("✅ 浏览器已关闭")
                except:
                    pass  # 忽略清理错误
                finally:
                    self.current_scraper = None
                    
        except Exception as e:
            self.log_message(f"⚠️ 快速清理时出错: {e}", "WARNING")
    
    def run(self):
        """运行GUI"""
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.log_message("🎯 多平台商品爬虫GUI监听器已启动")
        self.log_message("📋 支持平台: PDD、京东、淘宝、抖音")
        self.root.mainloop()


def show_token_dialog():
    """显示令牌输入对话框（现代化设计）"""
    import configparser
    from pathlib import Path
    
    config_file = Path.home() / '.shopspider' / 'config.ini'
    
    # 尝试加载保存的令牌
    saved_token = ''
    if config_file.exists():
        try:
            config = configparser.ConfigParser()
            config.read(config_file, encoding='utf-8')
            saved_token = config.get('Auth', 'token', fallback='')
        except:
            pass
    
    # 创建对话框
    dialog = tk.Tk()
    dialog.title("水滴采集系统")
    dialog.geometry("600x520")
    dialog.resizable(False, False)
    
    # 设置背景渐变色
    dialog.configure(bg='#f0f4f8')
    
    # 居中显示
    screen_width = dialog.winfo_screenwidth()
    screen_height = dialog.winfo_screenheight()
    x = (screen_width - 600) // 2
    y = (screen_height - 520) // 2
    dialog.geometry(f"600x520+{x}+{y}")
    
    result = {'token': None, 'save': False}
    
    # 顶部装饰条（渐变效果）
    top_bar = tk.Frame(dialog, bg='#667eea', height=8)
    top_bar.pack(fill=tk.X)
    
    # 主容器（白色卡片效果）
    card_frame = tk.Frame(dialog, bg='white')
    card_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=(25, 20))
    
    # Logo/图标区域
    icon_frame = tk.Frame(card_frame, bg='white')
    icon_frame.pack(pady=(15, 8))
    
    # 使用Canvas创建圆形图标
    icon_canvas = tk.Canvas(icon_frame, width=70, height=70, bg='white', highlightthickness=0)
    icon_canvas.pack()
    
    # 绘制渐变圆形图标
    icon_canvas.create_oval(8, 8, 62, 62, fill='#667eea', outline='#764ba2', width=3)
    icon_canvas.create_text(35, 35, text='🔐', font=('Arial', 28))
    
    # 标题
    title_label = tk.Label(card_frame, text="水滴采集系统", 
                          font=('Microsoft YaHei UI', 20, 'bold'),
                          bg='white', fg='#2c3e50')
    title_label.pack(pady=(8, 5))
    
    subtitle_label = tk.Label(card_frame, text="请输入您的采集令牌以继续", 
                             font=('Microsoft YaHei UI', 10),
                             bg='white', fg='#7f8c8d')
    subtitle_label.pack(pady=(0, 20))
    
    # 输入区域容器
    input_container = tk.Frame(card_frame, bg='white')
    input_container.pack(fill=tk.X, padx=30, pady=(0, 8))
    
    # 令牌标签
    token_label = tk.Label(input_container, text="采集令牌", 
                          font=('Microsoft YaHei UI', 10),
                          bg='white', fg='#34495e')
    token_label.pack(anchor=tk.W, pady=(0, 6))
    
    # 令牌输入框（带边框效果）
    entry_frame = tk.Frame(input_container, bg='#e8f0fe', bd=0, relief=tk.FLAT)
    entry_frame.pack(fill=tk.X)
    
    token_entry = tk.Entry(entry_frame, font=('Consolas', 11), 
                          bg='#e8f0fe', fg='#2c3e50',
                          relief=tk.FLAT, bd=0,
                          insertbackground='#667eea')
    token_entry.pack(fill=tk.X, padx=15, pady=12)
    token_entry.insert(0, saved_token)
    token_entry.focus()
    
    # 输入框获得焦点时的效果
    def on_entry_focus_in(e):
        entry_frame.configure(bg='#d1e3f8')
    
    def on_entry_focus_out(e):
        entry_frame.configure(bg='#e8f0fe')
    
    token_entry.bind('<FocusIn>', on_entry_focus_in)
    token_entry.bind('<FocusOut>', on_entry_focus_out)
    
    # 保存令牌选项（美化的复选框）
    checkbox_frame = tk.Frame(card_frame, bg='white')
    checkbox_frame.pack(pady=(12, 8))
    
    save_var = tk.BooleanVar(value=bool(saved_token))
    save_check = tk.Checkbutton(checkbox_frame, text="  记住令牌（下次自动填充）", 
                               variable=save_var,
                               font=('Microsoft YaHei UI', 9),
                               bg='white', fg='#555',
                               activebackground='white',
                               selectcolor='white',
                               cursor='hand2')
    save_check.pack()
    
    # 错误/成功提示标签（固定高度确保按钮可见）
    message_label = tk.Label(card_frame, text="", 
                            font=('Microsoft YaHei UI', 10),
                            bg='white', wraplength=480,
                            height=3)
    message_label.pack(pady=(10, 10))
    
    def validate_token():
        """验证令牌"""
        token = token_entry.get().strip()
        
        if not token:
            message_label.config(text="❌ 请输入令牌", fg='#e74c3c')
            return
        
        # 连接Redis验证令牌
        try:
            message_label.config(text="⏳ 正在验证令牌...", fg='#3498db')
            dialog.update()
            
            client = redis.Redis(
                host='8.155.9.53',
                port=6300,
                password='jhkdjhkjdhsIUTYURTU_XF6dAk',
                decode_responses=False,
                socket_connect_timeout=5
            )
            
            # 获取任务数据
            task_data = client.get(':1:自动获取产品列表')
            
            if not task_data:
                message_label.config(text="❌ 您未注册登录水滴后台 或者 未申请采集任务", fg='#e74c3c')
                return
            
            # 解析数据
            try:
                tasks = pickle.loads(task_data)
                
                # 如果是字符串，解析JSON
                if isinstance(tasks, str):
                    tasks = json.loads(tasks)
                
                # 检查令牌是否存在
                if isinstance(tasks, dict):
                    if token not in tasks:
                        message_label.config(text="❌ 您未注册登录水滴后台 或者 未申请采集任务", fg='#e74c3c')
                        return
                    
                    user_tasks = tasks.get(token, [])
                    if not user_tasks or len(user_tasks) == 0:
                        message_label.config(text="❌ 未申请采集任务", fg='#e74c3c')
                        return
                    
                    # 验证成功
                    result['token'] = token
                    result['save'] = save_var.get()
                    
                    # 保存令牌（如果选中）
                    if result['save']:
                        try:
                            config_file.parent.mkdir(parents=True, exist_ok=True)
                            config = configparser.ConfigParser()
                            config['Auth'] = {'token': token}
                            with open(config_file, 'w', encoding='utf-8') as f:
                                config.write(f)
                        except Exception as e:
                            print(f"保存令牌失败: {e}")
                    
                    # 显示验证成功提示（优化样式）
                    message_label.config(text="✅ 令牌验证成功！正在启动程序...", fg='#27ae60')
                    
                    # 禁用按钮和输入框，防止重复操作
                    confirm_btn.config(state='disabled', bg='#95a5a6')
                    cancel_btn.config(state='disabled', bg='#95a5a6')
                    token_entry.config(state='disabled')
                    save_check.config(state='disabled')
                    
                    dialog.update()
                    
                    # 延迟2秒后关闭对话框
                    def close_dialog():
                        dialog.destroy()
                    
                    dialog.after(2000, close_dialog)
                    
                else:
                    # 旧格式（列表），不需要令牌验证
                    message_label.config(text="⚠️ 数据格式不兼容，请联系管理员", fg='#f39c12')
                    return
                    
            except Exception as e:
                message_label.config(text=f"❌ 数据解析失败: {str(e)[:50]}", fg='#e74c3c')
                return
                
        except redis.ConnectionError:
            message_label.config(text="❌ 无法连接到服务器，请检查网络", fg='#e74c3c')
        except Exception as e:
            message_label.config(text=f"❌ 验证失败: {str(e)[:50]}", fg='#e74c3c')
    
    def on_cancel():
        """取消"""
        dialog.destroy()
    
    # 按钮框架（确保显示）
    button_frame = tk.Frame(card_frame, bg='white')
    button_frame.pack(side=tk.BOTTOM, pady=(5, 15))
    
    # 现代化按钮样式
    confirm_btn = tk.Button(button_frame, text="确认登录", command=validate_token, 
                           width=16, height=2, 
                           font=('Microsoft YaHei UI', 10, 'bold'),
                           bg='#667eea', fg='white', 
                           cursor='hand2',
                           relief=tk.RAISED, bd=1,
                           activebackground='#5568d3',
                           activeforeground='white')
    confirm_btn.pack(side=tk.LEFT, padx=10)
    
    cancel_btn = tk.Button(button_frame, text="退出程序", command=on_cancel,
                          width=16, height=2, 
                          font=('Microsoft YaHei UI', 10),
                          bg='#e0e0e0', fg='#333',
                          cursor='hand2',
                          relief=tk.RAISED, bd=1,
                          activebackground='#d0d0d0',
                          activeforeground='#333')
    cancel_btn.pack(side=tk.LEFT, padx=10)
    
    # 添加按钮悬停效果
    def on_confirm_enter(e):
        confirm_btn.config(bg='#5568d3')
    
    def on_confirm_leave(e):
        if confirm_btn['state'] == 'normal':
            confirm_btn.config(bg='#667eea')
    
    def on_cancel_enter(e):
        cancel_btn.config(bg='#d0d0d0')
    
    def on_cancel_leave(e):
        if cancel_btn['state'] == 'normal':
            cancel_btn.config(bg='#e0e0e0')
    
    confirm_btn.bind('<Enter>', on_confirm_enter)
    confirm_btn.bind('<Leave>', on_confirm_leave)
    cancel_btn.bind('<Enter>', on_cancel_enter)
    cancel_btn.bind('<Leave>', on_cancel_leave)
    
    # 绑定回车键
    token_entry.bind('<Return>', lambda e: validate_token())
    
    dialog.mainloop()
    
    return result['token']


if __name__ == "__main__":
    # 显示令牌输入对话框
    user_token = show_token_dialog()
    
    if user_token:
        # 令牌验证成功，启动主程序
        app = MultiPlatformGUIMonitor(user_token)
        app.run() 
    else:
        # 用户取消或验证失败
        print("程序已退出") 